import { equals, is } from "ramda";
import { AppCache } from "./cache.util";

/**
 * 时间转毫秒
 * 1. 1h -> 3600 * 1000
 * 2. 1m -> 60 * 1000
 * 3. 1s -> 1000
 * 4. 1h2m3s -> 3600 * 1000 + 60 * 1000 + 3000
 * @param time
 */
declare type TimeUnit = "d" | "h" | "m" | "s";
const TIME_MILLIS = {
  d: 24 * 3600 * 1000,
  h: 3600 * 1000,
  m: 60 * 1000,
  s: 1000,
};
export const time2Millis = (time: string | number) => {
  if (is(Number, time)) return time;
  const reg = /(\d+)([dhms])/g;
  let result = 0;
  let match = reg.exec(time);
  while (match) {
    const [, num, unit] = match;
    result += Number(num) * TIME_MILLIS[unit as TimeUnit];
    match = reg.exec(time);
  }
  return result;
};

/**
 * 一个简易的 Promise 错误处理函数
 * @param promise 要处理的 Promise
 */
export const to = async <T>(promise: Promise<T>): Promise<[any, T | undefined]> => {
  return new Promise((resolve) => {
    promise.then((resp) => resolve([undefined, resp])).catch((err) => resolve([err, undefined]));
  });
};

/**
 * api 缓存
 * @param apiFunc api
 * @param genKey key生成方式 默认对所有参数做json
 * @param cacheTime 缓存时间
 * @param timeout 超时时间
 * @param waitInterval 轮训等待时间
 */
export const apiCacheWrapper = <R, T extends any[]>(
  apiFunc: (...args: T) => Promise<R>,
  genKey: (...args: T) => any = (...args: T) => JSON.stringify(args),
  cacheTime: string | number = 5000,
  timeout: string | number = 5000,
  waitInterval: string | number = 100,
) => {
  const running = new AppCache(timeout, "5m");
  const result = new AppCache(cacheTime, "5m", true);
  const errResult = new AppCache(Math.min(time2Millis(cacheTime), time2Millis("5s")), "5m");
  return async (...args: T): Promise<R> => {
    const key = genKey(...args);
    if (result.has(key)) return result.get(key, undefined);
    if (errResult.has(key)) return Promise.reject(errResult.get(key, undefined));
    while (running.has(key)) {
      await sleep(waitInterval);
      if (result.has(key)) return result.get(key, undefined);
      if (errResult.has(key)) return Promise.reject(errResult.get(key, undefined));
    }
    running.set(key, true);
    const [err, res] = await to(apiFunc(...args));
    running.remove(key);
    if (err) {
      errResult.set(key, err);
      return Promise.reject(err);
    }
    result.set(key, res);
    return result.get(key);
  };
};

/**
 * 休眠
 * @param time 时间
 */
const __sleep = (time: number) => new Promise((r) => setTimeout(r, time));
const __10d = time2Millis("10d");
export const sleep = (time: string | number) => {
  let t = time2Millis(time);
  return new Promise(async (resolve) => {
    let n = Math.floor(t / __10d);
    while (n-- > 0) await __sleep(__10d);
    await __sleep(t % __10d);
    resolve(true);
  });
};

/**
 * 随机字符串
 * @param len 长度
 * @param pool 字符池
 */
export const randomStr = (
  len: number = 6,
  pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
) => {
  const ans = [];
  while (len-- > 0) {
    ans.push(pool[Math.floor(Math.random() * pool.length)]);
  }
  return ans.join("");
};

/**
 * 随机数
 * @param min 最小
 * @param max 最大
 */
export const randomNumber = (min: number, max: number) => {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

/**
 * 获取当前环境名称
 */
export const getProcessEnv = () =>
  ((process.env.NODE_ENV as string) || "production").trim() as
    | "development"
    | "production"
    | "test";

/**
 * 与配置的环境名称比较
 */
const equalsEnv = equals(getProcessEnv());
/**
 * 是否是开发环境
 */
export const isDev = () => equalsEnv("development");
/**
 * 是否是生产环境
 */
export const isProd = () => equalsEnv("production");
/**
 * 是否是测试环境
 */
export const isTest = () => equalsEnv("test");
